home *** CD-ROM | disk | FTP | other *** search
/ The Very Best of Atari Inside / The Very Best of Atari Inside 1.iso / mint / mint99s / dosdir.c < prev    next >
C/C++ Source or Header  |  1993-01-16  |  30KB  |  1,305 lines

  1. /*
  2. Copyright 1990,1991,1992 Eric R. Smith.
  3. Copyright 1992,1993 Atari Corporation.
  4. All rights reserved.
  5. */
  6.  
  7. /* DOS directory functions */
  8.  
  9. #include "mint.h"
  10.  
  11. /* change to a new drive: should always return a map of valid drives */
  12.  
  13. long ARGS_ON_STACK
  14. d_setdrv(d)
  15.     int d;
  16. {
  17.     long r;
  18.     extern long dosdrvs;    /* in filesys.c */
  19.  
  20.     r = drvmap() | dosdrvs | PSEUDODRVS;
  21.  
  22.     TRACE(("Dsetdrv(%d)", d));
  23.     if (d < 0 || d >= NUM_DRIVES || (r & (1L << d)) == 0) {
  24.         DEBUG(("Dsetdrv: invalid drive %d", d));
  25.         return r;
  26.     }
  27.  
  28.     curproc->base->p_defdrv = curproc->curdrv = d;
  29.     return r;
  30. }
  31.  
  32.  
  33. long ARGS_ON_STACK
  34. d_getdrv()
  35. {
  36.     TRACE(("Dgetdrv"));
  37.     return curproc->curdrv;
  38. }
  39.  
  40. long ARGS_ON_STACK
  41. d_free(buf, d)
  42.     long *buf;
  43.     int d;
  44. {
  45.     fcookie *dir = 0;
  46.     extern int aliasdrv[];
  47.  
  48.     TRACE(("Dfree(%d)", d));
  49.  
  50. /* drive 0 means current drive, otherwise it's d-1 */
  51.     if (d)
  52.         d = d-1;
  53.     else
  54.         d = curproc->curdrv;
  55.  
  56. /* Hack to make programs (like df) which use drive
  57.  * information from Fxattr() work more often.
  58.  * BUG: this works only if the drive is a root,
  59.  * a current directory or an alias, or one of the
  60.  * standard drives.
  61.  */
  62.     if (d < 0 || d >= NUM_DRIVES) {
  63.         int i;
  64.  
  65.         for (i = 0; i < NUM_DRIVES; i++) {
  66.             if (aliasdrv[i] == d) {
  67.                 d = i;
  68.                 goto aliased;
  69.             }
  70.             if (curproc->curdir[i].dev == d) {
  71.                 dir = &curproc->curdir[i];
  72.             } else if (curproc->root[i].dev == d) {
  73.                 dir = &curproc->root[i];
  74.             }
  75.         }
  76.         if (dir && dir->fs) {
  77.             return (*dir->fs->dfree)(dir, buf);
  78.         }
  79.         return EDRIVE;
  80.     }
  81.  
  82. /* check for a media change -- we don't care much either way, but it
  83.  * does keep the results more accurate
  84.  */
  85.     (void)disk_changed(d);
  86.  
  87. aliased:
  88.  
  89. /* use current directory, not root, since it's more likely that
  90.  * programs are interested in the latter (this makes U: work much
  91.  * better)
  92.  */
  93.     dir = &curproc->curdir[d];
  94.     if (!dir->fs) {
  95.         DEBUG(("Dfree: bad drive"));
  96.         return EDRIVE;
  97.     }
  98.  
  99.     return (*dir->fs->dfree)(dir, buf);
  100. }
  101.  
  102. /* temp1 is a convenient place for path2fs puts the last component of
  103.  *   the path name
  104.  */
  105.  
  106. extern char temp1[PATH_MAX];    /* in filesys.c */
  107.  
  108. long ARGS_ON_STACK
  109. d_create(path)
  110.     const char *path;
  111. {
  112.     fcookie dir;
  113.     long r;
  114.  
  115.     TRACE(("Dcreate(%s)", path));
  116.  
  117.     r = path2cookie(path, temp1, &dir);
  118.     if (r) {
  119.         DEBUG(("Dcreate(%s): returning %ld", path, r));
  120.         return r;    /* an error occured */
  121.     }
  122. /* check for write permission on the directory */
  123.     r = dir_access(&dir, S_IWOTH);
  124.     if (r) {
  125.         DEBUG(("Dcreate(%s): access to directory denied",path));
  126.         release_cookie(&dir);
  127.         return r;
  128.     }
  129.     r = (*dir.fs->mkdir)(&dir, temp1, DEFAULT_DIRMODE & ~curproc->umask);
  130.     release_cookie(&dir);
  131.     return r;
  132. }
  133.  
  134. long ARGS_ON_STACK
  135. d_delete(path)
  136.     const char *path;
  137. {
  138.     fcookie parentdir, targdir;
  139.     long r;
  140.     PROC *p;
  141.     int i;
  142.     XATTR xattr;
  143.  
  144.     TRACE(("Ddelete(%s)", path));
  145.  
  146.     r = path2cookie(path, temp1, &parentdir);
  147.  
  148.     if (r) {
  149.         DEBUG(("Ddelete(%s): error %lx", path, r));
  150.         release_cookie(&parentdir);
  151.         return r;
  152.     }
  153. /* check for write permission on the directory which the target
  154.  * is located
  155.  */
  156.     if ((r = dir_access(&parentdir, S_IWOTH)) != 0) {
  157.         DEBUG(("Ddelete(%s): access to directory denied", path));
  158.         release_cookie(&parentdir);
  159.         return r;
  160.     }
  161.  
  162. /* now get the info on the file itself */
  163.  
  164.     r = relpath2cookie(&parentdir, temp1, NULL, &targdir, 0);
  165.     if (r) {
  166. bailout:
  167.         release_cookie(&parentdir);
  168.         DEBUG(("Ddelete: error %ld on %s", r, path));
  169.         return r;
  170.     }
  171.     if ((r = (*targdir.fs->getxattr)(&targdir, &xattr)) != 0) {
  172.         release_cookie(&targdir);
  173.         goto bailout;
  174.     }
  175.  
  176. /* if the "directory" is a symbolic link, really unlink it */
  177.     if ( (xattr.mode & S_IFMT) == S_IFLNK ) {
  178.         r = (*parentdir.fs->remove)(&parentdir, temp1);
  179.     } else if ( (xattr.mode & S_IFMT) != S_IFDIR ) {
  180.         DEBUG(("Ddelete: %s is not a directory", path));
  181.         r = EPTHNF;
  182.     } else {
  183.  
  184. /* don't delete anyone else's root or current directory */
  185.         for (p = proclist; p; p = p->gl_next) {
  186.         if (p->wait_q == ZOMBIE_Q || p->wait_q == TSR_Q)
  187.             continue;
  188.         for (i = 0; i < NUM_DRIVES; i++) {
  189.             if (samefile(&targdir, &p->root[i])) {
  190.                 DEBUG(("Ddelete: directory %s is a root directory",
  191.                     path));
  192. noaccess:
  193.                 release_cookie(&targdir);
  194.                 release_cookie(&parentdir);
  195.                 return EACCDN;
  196.             } else if (samefile(&targdir, &p->curdir[i])) {
  197.                 if (i == p->curdrv && p != curproc) {
  198.                     DEBUG(("Ddelete: directory %s is in use",
  199.                         path));
  200.                     goto noaccess;
  201.                 } else {
  202.                     release_cookie(&p->curdir[i]);
  203.                     dup_cookie(&p->curdir[i], &p->root[i]);
  204.                 } 
  205.             }
  206.         }
  207.         }
  208.         release_cookie(&targdir);
  209.         r = (*parentdir.fs->rmdir)(&parentdir, temp1);
  210.     }
  211.     release_cookie(&parentdir);
  212.     return r;
  213. }
  214.  
  215. long ARGS_ON_STACK
  216. d_setpath(path)
  217.     const char *path;
  218. {
  219.     fcookie dir;
  220.     int drv = curproc->curdrv;
  221.     int i;
  222.     char c;
  223.     long r;
  224.     XATTR xattr;
  225.  
  226.     TRACE(("Dsetpath(%s)", path));
  227.  
  228.     r = path2cookie(path, follow_links, &dir);
  229.  
  230.     if (r) {
  231.         DEBUG(("Dsetpath(%s): returning %ld", path, r));
  232.         return r;
  233.     }
  234.  
  235.     if (path[0] && path[1] == ':') {
  236.         c = *path;
  237.         if (c >= 'a' && c <= 'z')
  238.             drv = c-'a';
  239.         else if (c >= 'A' && c <= 'Z')
  240.             drv = c-'A';
  241.     }
  242.  
  243.     r = (*dir.fs->getxattr)(&dir, &xattr);
  244.  
  245.     if (r < 0) {
  246.         DEBUG(("Dsetpath: file '%s': attributes not found", path));
  247.         release_cookie(&dir);
  248.         return r;
  249.     }
  250.  
  251.     if (!(xattr.attr & FA_DIR)) {
  252.         DEBUG(("Dsetpath(%s): not a directory",path));
  253.         release_cookie(&dir);
  254.         return EPTHNF;
  255.     }
  256.  
  257. /*
  258.  * watch out for symbolic links; if c:\foo is a link to d:\bar, then
  259.  * "cd c:\foo" should also change the drive to d:
  260.  */
  261.     if (drv != UNIDRV && dir.dev != curproc->root[drv].dev) {
  262.         for (i = 0; i < NUM_DRIVES; i++) {
  263.             if (curproc->root[i].dev == dir.dev &&
  264.                 curproc->root[i].fs == dir.fs) {
  265.                 if (drv == curproc->curdrv)
  266.                     curproc->curdrv = i;
  267.                 drv = i;
  268.                 break;
  269.             }
  270.         }
  271.     }
  272.     release_cookie(&curproc->curdir[drv]);
  273.     curproc->curdir[drv] = dir;
  274.     return 0;
  275. }
  276.  
  277. /* jr: like d_getpath, except that the caller provides a limit
  278.    for the max. number of characters to be put into the buffer.
  279.    Inspired by POSIX.1, getcwd(), 5.2.2 */
  280.  
  281. long ARGS_ON_STACK
  282. d_getcwd(path, drv, size)
  283.     char *path;
  284.     int drv, size;
  285. {
  286.     fcookie *dir, *root;
  287.     long r;
  288.     char buf[PATH_MAX];
  289.     FILESYS *fs;
  290.  
  291.     TRACE(("Dgetcwd(%c, %d)", drv + '@', size));
  292.     if (drv < 0 || drv > NUM_DRIVES)
  293.         return EDRIVE;
  294.  
  295.     drv = (drv == 0) ? curproc->curdrv : drv-1;
  296.  
  297.     root = &curproc->root[drv];
  298.  
  299.     if (!root->fs) {    /* maybe not initialized yet? */
  300.         changedrv(drv);
  301.         root = &curproc->curdir[drv];
  302.         if (!root->fs)
  303.             return EDRIVE;
  304.     }
  305.     fs = root->fs;
  306.     dir = &curproc->curdir[drv];
  307.  
  308.     if (!(fs->fsflags & FS_LONGPATH)) {
  309.         r = (*fs->getname)(root, dir, buf, PATH_MAX);
  310.         if (r) return r;
  311.         if (strlen(buf) < size) {
  312.             strcpy(path, buf);
  313.             return 0;
  314.         } else {
  315.             return PATH_MAX;
  316.         }
  317.     }
  318.     return (*fs->getname)(root, dir, path, size);
  319. }
  320.  
  321. long ARGS_ON_STACK
  322. d_getpath(path, drv)
  323.     char *path;
  324.     int drv;
  325. {
  326.     TRACE(("Dgetpath(%c)", drv + '@'));
  327.     return d_getcwd(path, drv, PATH_MAX);
  328. }
  329.  
  330. long ARGS_ON_STACK
  331. f_setdta(dta)
  332.     DTABUF *dta;
  333. {
  334.  
  335.     TRACE(("Fsetdta: %lx", dta));
  336.     curproc->dta = dta;
  337.     curproc->base->p_dta = (char *)dta;
  338.     return 0;
  339. }
  340.  
  341. long ARGS_ON_STACK
  342. f_getdta()
  343. {
  344.     long r;
  345.  
  346.     r = (long)curproc->dta;
  347.     TRACE(("Fgetdta: returning %lx", r));
  348.     return r;
  349. }
  350.  
  351. /*
  352.  * Fsfirst/next are actually implemented in terms of opendir/readdir/closedir.
  353.  */
  354.  
  355. long ARGS_ON_STACK
  356. f_sfirst(path, attrib)
  357.     const char *path;
  358.     int attrib;
  359. {
  360.     char *s, *slash;
  361.     FILESYS *fs;
  362.     fcookie dir, newdir;
  363.     DTABUF *dta;
  364.     DIR *dirh;
  365.     XATTR xattr;
  366.     long r;
  367.     int i, havelabel;
  368.  
  369.     TRACE(("Fsfirst(%s, %x)", path, attrib));
  370.  
  371.     r = path2cookie(path, temp1, &dir);
  372.  
  373.     if (r) {
  374.         DEBUG(("Fsfirst(%s): path2cookie returned %ld", path, r));
  375.         return r;
  376.     }
  377.  
  378. /*
  379.  * we need to split the last name (which may be a pattern) off from
  380.  * the rest of the path, even if FS_KNOPARSE is true
  381.  */
  382.     slash = 0;
  383.     s = temp1;
  384.     while (*s) {
  385.         if (*s == '\\')
  386.             slash = s;
  387.         s++;
  388.     }
  389.  
  390.     if (slash) {
  391.         *slash++ = 0;    /* slash now points to a name or pattern */
  392.         r = relpath2cookie(&dir, temp1, follow_links, &newdir, 0);
  393.         release_cookie(&dir);
  394.         if (r) {
  395.             DEBUG(("Fsfirst(%s): lookup returned %ld", path, r));
  396.             return r;
  397.         }
  398.         dir = newdir;
  399.     } else {
  400.         slash = temp1;
  401.     }
  402.  
  403. /* BUG? what if there really is an empty file name? */
  404.     if (!*slash) {
  405.         DEBUG(("Fsfirst: empty pattern"));
  406.         return EFILNF;
  407.     }
  408.  
  409.     fs = dir.fs;
  410.     dta = curproc->dta;
  411.  
  412. /* Now, see if we can find a DIR slot for the search. We use the following
  413.  * heuristics to try to avoid destroying a slot:
  414.  * (1) if the search doesn't use wildcards, don't bother with a slot
  415.  * (2) if an existing slot was for the same DTA address, re-use it
  416.  * (3) if there's a free slot, re-use it. Slots are freed when the
  417.  *     corresponding search is terminated.
  418.  */
  419.  
  420.     for (i = 0; i < NUM_SEARCH; i++) {
  421.         if (curproc->srchdta[i] == dta) {
  422.             dirh = &curproc->srchdir[i];
  423.             if (dirh->fc.fs) {
  424.                 (*dirh->fc.fs->closedir)(dirh);
  425.                 release_cookie(&dirh->fc);
  426.                 dirh->fc.fs = 0;
  427.             }
  428.             curproc->srchdta[i] = 0; /* slot is now free */
  429.         }
  430.     }
  431.  
  432. /* copy the pattern over into dta_pat into TOS 8.3 form */
  433. /* remember that "slash" now points at the pattern (it follows the last \,
  434.    if any)
  435.  */
  436.     copy8_3(dta->dta_pat, slash);
  437.  
  438. /* if attrib & FA_LABEL, read the volume label */
  439. /* BUG: the label date and time are wrong. Does it matter?
  440.  */
  441.     havelabel = 0;
  442.     if (attrib & FA_LABEL) {
  443.         r = (*fs->readlabel)(&dir, dta->dta_name, TOS_NAMELEN+1);
  444.         dta->dta_attrib = FA_LABEL;
  445.         dta->dta_time = dta->dta_date = 0;
  446.         dta->dta_size = 0;
  447.         dta->magic = EVALID;
  448.         if (r == 0 && !pat_match(dta->dta_name, dta->dta_pat))
  449.             r = EFILNF;
  450.         if (attrib == FA_LABEL)
  451.             return r;
  452.         else if (r == 0)
  453.             havelabel = 1;
  454.     }
  455.  
  456.     if (!havelabel && has_wild(slash) == 0) { /* no wild cards in pattern */
  457.         r = relpath2cookie(&dir, slash, follow_links, &newdir, 0);
  458.         if (r == 0) {
  459.             r = (*newdir.fs->getxattr)(&newdir, &xattr);
  460.             release_cookie(&newdir);
  461.         }
  462.         release_cookie(&dir);
  463.         if (r) {
  464.             DEBUG(("Fsfirst(%s): couldn't get file attributes",path));
  465.             return r;
  466.         }
  467.         dta->magic = EVALID;
  468.         dta->dta_attrib = xattr.attr;
  469.         dta->dta_time = xattr.mtime;
  470.         dta->dta_date = xattr.mdate;
  471.         dta->dta_size = xattr.size;
  472.         strncpy(dta->dta_name, slash, TOS_NAMELEN-1);
  473.         dta->dta_name[TOS_NAMELEN-1] = 0;
  474.         if (curproc->domain == DOM_TOS &&
  475.             !(fs->fsflags & FS_CASESENSITIVE))
  476.             strupr(dta->dta_name);
  477.         return 0;
  478.     }
  479.  
  480. /* There is a wild card. Try to find a slot for an opendir/readdir
  481.  * search. NOTE: we also come here if we were asked to search for
  482.  * volume labels and found one.
  483.  */
  484.     for (i = 0; i < NUM_SEARCH; i++) {
  485.         if (curproc->srchdta[i] == 0)
  486.             break;
  487.     }
  488.     if (i == NUM_SEARCH) {
  489.         int oldest = 0; long oldtime = curproc->srchtim[0];
  490.  
  491.         DEBUG(("Fsfirst(%s): having to re-use a directory slot!",path));
  492.         for (i = 1; i < NUM_SEARCH; i++) {
  493.             if (curproc->srchtim[i] < oldtime) {
  494.                 oldest = i;
  495.                 oldtime = curproc->srchtim[i];
  496.             }
  497.         }
  498.     /* OK, close this directory for re-use */
  499.         i = oldest;
  500.         dirh = &curproc->srchdir[i];
  501.         if (dirh->fc.fs) {
  502.             (*dirh->fc.fs->closedir)(dirh);
  503.             release_cookie(&dirh->fc);
  504.             dirh->fc.fs = 0;
  505.         }
  506.         curproc->srchdta[i] = 0;
  507.     }
  508.  
  509. /* check to see if we have read permission on the directory (and make
  510.  * sure that it really is a directory!)
  511.  */
  512.     r = dir_access(&dir, S_IROTH);
  513.     if (r) {
  514.         DEBUG(("Fsfirst(%s): access to directory denied (error code %ld)", path, r));
  515.         release_cookie(&dir);
  516.         return r;
  517.     }
  518.  
  519. /* set up the directory for a search */
  520.     dirh = &curproc->srchdir[i];
  521.     dirh->fc = dir;
  522.     dirh->index = 0;
  523.     dirh->flags = TOS_SEARCH;
  524.     r = (*dir.fs->opendir)(dirh, dirh->flags);
  525.     if (r != 0) {
  526.         DEBUG(("Fsfirst(%s): couldn't open directory (error %ld)",
  527.             path, r));
  528.         release_cookie(&dir);
  529.         return r;
  530.     }
  531.  
  532. /* mark the slot as in-use */
  533.     curproc->srchdta[i] = dta;
  534.  
  535. /* set up the DTA for Fsnext */
  536.     dta->index = i;
  537.     dta->magic = SVALID;
  538.     dta->dta_sattrib = attrib;
  539.  
  540. /* OK, now basically just do Fsnext, except that instead of ENMFIL we
  541.  * return EFILNF.
  542.  * NOTE: If we already have found a volume label from the search above,
  543.  * then we skip the f_snext and just return that.
  544.  */
  545.     if (havelabel)
  546.         return 0;
  547.  
  548.     r = f_snext();
  549.     if (r == ENMFIL) r = EFILNF;
  550.     if (r)
  551.         TRACE(("Fsfirst: returning %ld", r));
  552. /* release_cookie isn't necessary, since &dir is now stored in the
  553.  * DIRH structure and will be released when the search is completed
  554.  */
  555.     return r;
  556. }
  557.  
  558. /*
  559.  * Counter for Fsfirst/Fsnext, so that we know which search slots are
  560.  * least recently used. This is updated once per second by the code
  561.  * in timeout.c.
  562.  * BUG: 1/second is pretty low granularity
  563.  */
  564.  
  565. long searchtime;
  566.  
  567. long ARGS_ON_STACK
  568. f_snext()
  569. {
  570.     static char buf[TOS_NAMELEN+1];
  571.     DTABUF *dta = curproc->dta;
  572.     FILESYS *fs;
  573.     fcookie fc;
  574.     int i;
  575.     DIR *dirh;
  576.     long r;
  577.     XATTR xattr;
  578.  
  579.     TRACE(("Fsnext"));
  580.  
  581.     if (dta->magic == EVALID) {
  582.         DEBUG(("Fsnext: DTA marked a failing search"));
  583.         return ENMFIL;
  584.     }
  585.     if (dta->magic != SVALID) {
  586.         DEBUG(("Fsnext: dta incorrectly set up"));
  587.         return EINVFN;
  588.     }
  589.  
  590.     i = dta->index;
  591.     dirh = &curproc->srchdir[i];
  592.     curproc->srchtim[i] = searchtime;
  593.  
  594.     fs = dirh->fc.fs;
  595.     if (!fs)        /* oops -- the directory got closed somehow */
  596.         return EINTRN;
  597.  
  598. /* BUG: f_snext and readdir should check for disk media changes */
  599.  
  600.     for(;;) {
  601.         r = (*fs->readdir)(dirh, buf, TOS_NAMELEN+1, &fc);
  602.  
  603.         if (r == ENAMETOOLONG) {
  604.             DEBUG(("Fsnext: name too long"));
  605.             continue;    /* TOS programs never see these names */
  606.         }
  607.         if (r != 0) {
  608. baderror:
  609.             if (dirh->fc.fs)
  610.                 (void)(*fs->closedir)(dirh);
  611.             release_cookie(&dirh->fc);
  612.             dirh->fc.fs = 0;
  613.             curproc->srchdta[i] = 0;
  614.             dta->magic = EVALID;
  615.             if (r != ENMFIL)
  616.                 DEBUG(("Fsnext: returning %ld", r));
  617.             return r;
  618.         }
  619.  
  620.         if (!pat_match(buf, dta->dta_pat))
  621.             continue;    /* different patterns */
  622.  
  623.     /* check for search attributes */
  624.         r = (*fc.fs->getxattr)(&fc, &xattr);
  625.         release_cookie(&fc);
  626.         if (r) {
  627.             DEBUG(("Fsnext: couldn't get file attributes"));
  628.             goto baderror;
  629.         }
  630.     /* if the file is a symbolic link, try to find what it's linked to */
  631.         if ( (xattr.mode & S_IFMT) == S_IFLNK ) {
  632.             char linkedto[PATH_MAX];
  633.             r = (*fc.fs->readlink)(&fc, linkedto, PATH_MAX);
  634.             if (r == 0) {
  635.             /* the "1" tells relpath2cookie that we read a link */
  636.                 r = relpath2cookie(&dirh->fc, linkedto,
  637.                     follow_links, &fc, 1);
  638.                 if (r == 0)
  639.                 r = (*fc.fs->getxattr)(&fc, &xattr);
  640.             }
  641.             if (r) {
  642.                 DEBUG(("Fsnext: couldn't follow link: error %ld",
  643.                     r));
  644.             }
  645.         }
  646.  
  647.     /* silly TOS rules for matching attributes */
  648.         if (xattr.attr == 0) break;
  649.         if (xattr.attr & 0x21) break;
  650.         if (dta->dta_sattrib & xattr.attr)
  651.             break;
  652.     }
  653.  
  654. /* here, we have a match */
  655.     dta->dta_attrib = xattr.attr;
  656.     dta->dta_time = xattr.mtime;
  657.     dta->dta_date = xattr.mdate;
  658.     dta->dta_size = xattr.size;
  659.     strcpy(dta->dta_name, buf);
  660.  
  661.     if (curproc->domain == DOM_TOS && !(fs->fsflags & FS_CASESENSITIVE)) {
  662.         strupr(dta->dta_name);
  663.     }
  664.     return 0;
  665. }
  666.  
  667. long ARGS_ON_STACK
  668. f_attrib(name, rwflag, attr)
  669.     const char *name;
  670.     int rwflag;
  671.     int attr;
  672. {
  673.     fcookie fc;
  674.     XATTR xattr;
  675.     long r;
  676.  
  677.     TRACE(("Fattrib(%s, %d)", name, attr));
  678.  
  679.     r = path2cookie(name, (char *)0, &fc);
  680.  
  681.     if (r) {
  682.         DEBUG(("Fattrib(%s): error %ld", name, r));
  683.         return r;
  684.     }
  685.  
  686.     r = (*fc.fs->getxattr)(&fc, &xattr);
  687.     release_cookie(&fc);
  688.  
  689.     if (r) {
  690.         DEBUG(("Fattrib(%s): getxattr returned %ld", name, r));
  691.         return r;
  692.     }
  693.  
  694.     if (rwflag) {
  695.         if (attr & (FA_LABEL|FA_DIR)) {
  696.             DEBUG(("Fattrib(%s): illegal attributes specified",name));
  697.             return EACCDN;
  698.         } else if (curproc->euid && curproc->euid != xattr.uid) {
  699.             DEBUG(("Fattrib(%s): not the file's owner",name));
  700.             return EACCDN;
  701.         } else if (xattr.attr & (FA_LABEL|FA_DIR)) {
  702.             DEBUG(("Fattrib(%s): file is a volume label "
  703.                   "or directory",name));
  704.             return EACCDN;
  705.         }
  706.         return (*fc.fs->chattr)(&fc, attr);
  707.     } else {
  708.         return xattr.attr;
  709.     }
  710. }
  711.  
  712. long ARGS_ON_STACK
  713. f_delete(name)
  714.     const char *name;
  715. {
  716.     fcookie dir;
  717.     long r;
  718.  
  719.     TRACE(("Fdelete(%s)", name));
  720.  
  721.     r = path2cookie(name, temp1, &dir);
  722.  
  723.     if (r) {
  724.         DEBUG(("Fdelete: error %ld", r));
  725.         return r;
  726.     }
  727.  
  728. /* check for write permission on directory */
  729.     r = dir_access(&dir, S_IWOTH);
  730.     if (r) {
  731.         DEBUG(("Fdelete(%s): write access to directory denied",name));
  732.     } else {
  733. /* BUG: we should check here for a read-only file */
  734.         r = (*dir.fs->remove)(&dir,temp1);
  735.     }
  736.     release_cookie(&dir);
  737.     return r;
  738. }
  739.  
  740. long ARGS_ON_STACK
  741. f_rename(junk, old, new)
  742.     int junk;        /* ignored, for TOS compatibility */
  743.     const char *old, *new;
  744. {
  745.     fcookie olddir, newdir, oldfil;
  746.     XATTR xattr;
  747.     char temp2[PATH_MAX];
  748.     long r;
  749.  
  750.     UNUSED(junk);
  751.  
  752.     TRACE(("Frename(%s, %s)", old, new));
  753.  
  754.     r = path2cookie(old, temp2, &olddir);
  755.     if (r) {
  756.         DEBUG(("Frename(%s,%s): error parsing old name",old,new));
  757.         return r;
  758.     }
  759. /* check for permissions on the old file
  760.  * GEMDOS doesn't allow rename if the file is FA_RDONLY
  761.  * we enforce this restriction only on regular files; processes,
  762.  * directories, and character special files can be renamed at will
  763.  */
  764.     r = relpath2cookie(&olddir, temp2, follow_links, &oldfil, 0);
  765.     if (r) {
  766.         DEBUG(("Frename(%s,%s): old file not found",old,new));
  767.         release_cookie(&olddir);
  768.         return r;
  769.     }
  770.     r = (*oldfil.fs->getxattr)(&oldfil, &xattr);
  771.     if (r ||
  772.         ((xattr.mode & S_IFMT) == S_IFREG && (xattr.attr & FA_RDONLY)) )
  773.     {
  774.         DEBUG(("Frename(%s,%s): access to old file not granted",old,new));
  775.         release_cookie(&olddir);
  776.         release_cookie(&oldfil);
  777.         return EACCDN;
  778.     }
  779.     r = path2cookie(new, temp1, &newdir);
  780.     if (r) {
  781.         DEBUG(("Frename(%s,%s): error parsing new name",old,new));
  782.         release_cookie(&olddir);
  783.         release_cookie(&oldfil);
  784.         return r;
  785.     }
  786.  
  787.     if (newdir.fs != olddir.fs) {
  788.         DEBUG(("Frename(%s,%s): different file systems",old,new));
  789.         release_cookie(&olddir);
  790.         release_cookie(&oldfil);
  791.         release_cookie(&newdir);
  792.         return EXDEV;    /* cross device rename */
  793.     }
  794.  
  795. /* check for write permission on both directories */
  796.     r = dir_access(&olddir, S_IWOTH);
  797.     if (!r) r = dir_access(&newdir, S_IWOTH);
  798.     if (r) {
  799.         DEBUG(("Frename(%s,%s): access to a directory denied",old,new));
  800.     } else {
  801.         r = (*newdir.fs->rename)(&olddir, temp2, &newdir, temp1);
  802.     }
  803.     release_cookie(&olddir);
  804.     release_cookie(&oldfil);
  805.     release_cookie(&newdir);
  806.     return r;
  807. }
  808.  
  809. /*
  810.  * GEMDOS extension: Dpathconf(name, which)
  811.  * returns information about filesystem-imposed limits; "name" is the name
  812.  * of a file or directory about which the limit information is requested;
  813.  * "which" is the limit requested, as follows:
  814.  *    -1    max. value of "which" allowed
  815.  *    0    internal limit on open files, if any
  816.  *    1    max. number of links to a file    {LINK_MAX}
  817.  *    2    max. path name length        {PATH_MAX}
  818.  *    3    max. file name length        {NAME_MAX}
  819.  *    4    no. of bytes in atomic write to FIFO {PIPE_BUF}
  820.  *    5    file name truncation rules
  821.  *    6    file name case translation rules
  822.  *
  823.  * unlimited values are returned as 0x7fffffffL
  824.  *
  825.  * see also Sysconf() in dos.c
  826.  */
  827.  
  828. long ARGS_ON_STACK
  829. d_pathconf(name, which)
  830.     const char *name;
  831.     int which;
  832. {
  833.     fcookie dir;
  834.     long r;
  835.  
  836.     r = path2cookie(name, (char *)0, &dir);
  837.     if (r) {
  838.         DEBUG(("Dpathconf(%s): bad path",name));
  839.         return r;
  840.     }
  841.     r = (*dir.fs->pathconf)(&dir, which);
  842.     if (which == DP_CASE && r == EINVFN) {
  843.     /* backward compatibility with old .XFS files */
  844.         r = (dir.fs->fsflags & FS_CASESENSITIVE) ? DP_CASESENS :
  845.                 DP_CASEINSENS;
  846.     }
  847.     release_cookie(&dir);
  848.     return r;
  849. }
  850.  
  851. /*
  852.  * GEMDOS extension: Opendir/Readdir/Rewinddir/Closedir offer a new,
  853.  * POSIX-like alternative to Fsfirst/Fsnext, and as a bonus allow for
  854.  * arbitrary length file names
  855.  */
  856.  
  857. long ARGS_ON_STACK
  858. d_opendir(name, flag)
  859.     const char *name;
  860.     int flag;
  861. {
  862.     DIR *dirh;
  863.     fcookie dir;
  864.     long r;
  865.  
  866.     r = path2cookie(name, follow_links, &dir);
  867.     if (r) {
  868.         DEBUG(("Dopendir(%s): error %ld", name, r));
  869.         return r;
  870.     }
  871.     r = dir_access(&dir, S_IROTH);
  872.     if (r) {
  873.         DEBUG(("Dopendir(%s): read permission denied", name));
  874.         release_cookie(&dir);
  875.         return r;
  876.     }
  877.  
  878.     dirh = (DIR *)kmalloc(SIZEOF(DIR));
  879.     if (!dirh) {
  880.         release_cookie(&dir);
  881.         return ENSMEM;
  882.     }
  883.  
  884.     dirh->fc = dir;
  885.     dirh->index = 0;
  886.     dirh->flags = flag;
  887.     r = (*dir.fs->opendir)(dirh, flag);
  888.     if (r) {
  889.         DEBUG(("d_opendir(%s): opendir returned %ld", name, r));
  890.         release_cookie(&dir);
  891.         kfree(dirh);
  892.         return r;
  893.     }
  894.  
  895. /* we keep a chain of open directories so that if a process
  896.  * terminates without closing them all, we can clean up
  897.  */
  898.     dirh->next = curproc->searches;
  899.     curproc->searches = dirh;
  900.  
  901.     return (long)dirh;
  902. }
  903.  
  904. long ARGS_ON_STACK
  905. d_readdir(len, handle, buf)
  906.     int len;
  907.     long handle;
  908.     char *buf;
  909. {
  910.     DIR *dirh = (DIR *)handle;
  911.     fcookie fc;
  912.     long r;
  913.  
  914.     if (!dirh->fc.fs)
  915.         return EIHNDL;
  916.     r = (*dirh->fc.fs->readdir)(dirh, buf, len, &fc);
  917.     if (r == 0)
  918.         release_cookie(&fc);
  919.     return r;
  920. }
  921.  
  922. long ARGS_ON_STACK
  923. d_rewind(handle)
  924.     long handle;
  925. {
  926.     DIR *dirh = (DIR *)handle;
  927.  
  928.     if (!dirh->fc.fs)
  929.         return EIHNDL;
  930.     return (*dirh->fc.fs->rewinddir)(dirh);
  931. }
  932.  
  933. /*
  934.  * NOTE: there is also code in terminate() in dosmem.c that
  935.  * does automatic closes of directory searches.
  936.  * If you change d_closedir(), you may also need to change
  937.  * terminate().
  938.  */
  939.  
  940. long ARGS_ON_STACK
  941. d_closedir(handle)
  942.     long handle;
  943. {
  944.     long r;
  945.     DIR *dirh = (DIR *)handle;
  946.     DIR **where;
  947.  
  948.     if (!dirh->fc.fs)
  949.         return EIHNDL;
  950.     where = &curproc->searches;
  951.     while (*where && *where != dirh) {
  952.         where = &((*where)->next);
  953.     }
  954.     if (!*where) {
  955.         DEBUG(("Dclosedir: not an open directory"));
  956.         return EIHNDL;
  957.     }
  958.  
  959. /* unlink the directory from the chain */
  960.     *where = dirh->next;
  961.  
  962.     r = (*dirh->fc.fs->closedir)(dirh);
  963.     release_cookie(&dirh->fc);
  964.     dirh->fc.fs = 0;
  965.  
  966.     if (r) {
  967.         DEBUG(("Dclosedir: error %ld", r));
  968.     }
  969.     kfree(dirh);
  970.     return r;
  971. }
  972.  
  973. /*
  974.  * GEMDOS extension: Fxattr gets extended attributes for a file. "flag"
  975.  * is 0 if symbolic links are to be followed (like stat), 1 if not (like
  976.  * lstat).
  977.  */
  978.  
  979. long ARGS_ON_STACK
  980. f_xattr(flag, name, xattr)
  981.     int flag;
  982.     const char *name;
  983.     XATTR *xattr;
  984. {
  985.     fcookie fc;
  986.     long r;
  987.  
  988.     TRACE(("Fxattr(%d, %s)", flag, name));
  989.  
  990.     r = path2cookie(name, flag ? (char *)0 : follow_links, &fc);
  991.     if (r) {
  992.         DEBUG(("Fxattr(%s): path2cookie returned %ld", name, r));
  993.         return r;
  994.     }
  995.     r = (*fc.fs->getxattr)(&fc, xattr);
  996.     if (r) {
  997.         DEBUG(("Fxattr(%s): returning %ld", name, r));
  998.     }
  999.     release_cookie(&fc);
  1000.     return r;
  1001. }
  1002.  
  1003. /*
  1004.  * GEMDOS extension: Flink(old, new) creates a hard link named "new"
  1005.  * to the file "old".
  1006.  */
  1007.  
  1008. long ARGS_ON_STACK
  1009. f_link(old, new)
  1010.     const char *old, *new;
  1011. {
  1012.     fcookie olddir, newdir;
  1013.     char temp2[PATH_MAX];
  1014.     long r;
  1015.  
  1016.     TRACE(("Flink(%s, %s)", old, new));
  1017.  
  1018.     r = path2cookie(old, temp2, &olddir);
  1019.     if (r) {
  1020.         DEBUG(("Flink(%s,%s): error parsing old name",old,new));
  1021.         return r;
  1022.     }
  1023.     r = path2cookie(new, temp1, &newdir);
  1024.     if (r) {
  1025.         DEBUG(("Flink(%s,%s): error parsing new name",old,new));
  1026.         release_cookie(&olddir);
  1027.         return r;
  1028.     }
  1029.  
  1030.     if (newdir.fs != olddir.fs) {
  1031.         DEBUG(("Flink(%s,%s): different file systems",old,new));
  1032.         release_cookie(&olddir);
  1033.         release_cookie(&newdir);
  1034.         return EXDEV;    /* cross device link */
  1035.     }
  1036.  
  1037. /* check for write permission on the destination directory */
  1038.  
  1039.     r = dir_access(&newdir, S_IWOTH);
  1040.     if (r) {
  1041.         DEBUG(("Flink(%s,%s): access to directory denied",old,new));
  1042.     } else
  1043.         r = (*newdir.fs->hardlink)(&olddir, temp2, &newdir, temp1);
  1044.     release_cookie(&olddir);
  1045.     release_cookie(&newdir);
  1046.     return r;
  1047. }
  1048.  
  1049. /*
  1050.  * GEMDOS extension: Fsymlink(old, new): create a symbolic link named
  1051.  * "new" that contains the path "old".
  1052.  */
  1053.  
  1054. long ARGS_ON_STACK
  1055. f_symlink(old, new)
  1056.     const char *old, *new;
  1057. {
  1058.     fcookie newdir;
  1059.     long r;
  1060.  
  1061.     TRACE(("Fsymlink(%s, %s)", old, new));
  1062.  
  1063.     r = path2cookie(new, temp1, &newdir);
  1064.     if (r) {
  1065.         DEBUG(("Fsymlink(%s,%s): error parsing %s", old,new,new));
  1066.         return r;
  1067.     }
  1068.     r = dir_access(&newdir, S_IWOTH);
  1069.     if (r) {
  1070.         DEBUG(("Fsymlink(%s,%s): access to directory denied",old,new));
  1071.     } else
  1072.         r = (*newdir.fs->symlink)(&newdir, temp1, old);
  1073.     release_cookie(&newdir);
  1074.     return r;
  1075. }
  1076.  
  1077. /*
  1078.  * GEMDOS extension: Freadlink(buflen, buf, linkfile):
  1079.  * read the contents of the symbolic link "linkfile" into the buffer
  1080.  * "buf", which has length "buflen".
  1081.  */
  1082.  
  1083. long ARGS_ON_STACK
  1084. f_readlink(buflen, buf, linkfile)
  1085.     int buflen;
  1086.     char *buf;
  1087.     const char *linkfile;
  1088. {
  1089.     fcookie file;
  1090.     long r;
  1091.     XATTR xattr;
  1092.  
  1093.     TRACE(("Freadlink(%s)", linkfile));
  1094.  
  1095.     r = path2cookie(linkfile, (char *)0, &file);
  1096.     if (r) {
  1097.         DEBUG(("Freadlink: unable to find %s", linkfile));
  1098.         return r;
  1099.     }
  1100.     r = (*file.fs->getxattr)(&file, &xattr);
  1101.     if (r) {
  1102.         DEBUG(("Freadlink: unable to get attributes for %s", linkfile));
  1103.     } else if ( (xattr.mode & S_IFMT) == S_IFLNK )
  1104.         r = (*file.fs->readlink)(&file, buf, buflen);
  1105.     else {
  1106.         DEBUG(("Freadlink: %s is not a link", linkfile));
  1107.         r = EACCDN;
  1108.     }
  1109.     release_cookie(&file);
  1110.     return r;
  1111. }
  1112.  
  1113. /*
  1114.  * GEMDOS extension: Dcntl(): do file system specific functions
  1115.  */
  1116.  
  1117. long ARGS_ON_STACK
  1118. d_cntl(cmd, name, arg)
  1119.     int cmd;
  1120.     const char *name;
  1121.     long arg;
  1122. {
  1123.     fcookie dir;
  1124.     long r;
  1125.  
  1126.     TRACE(("Dcntl(cmd=%x, file=%s, arg=%lx)", cmd, name, arg));
  1127.  
  1128.     r = path2cookie(name, temp1, &dir);
  1129.     if (r) {
  1130.         DEBUG(("Dcntl: couldn't find %s", name));
  1131.         return r;
  1132.     }
  1133.     r = (*dir.fs->fscntl)(&dir, temp1, cmd, arg);
  1134.     release_cookie(&dir);
  1135.     return r;
  1136. }
  1137.  
  1138. /*
  1139.  * GEMDOS extension: Fchown(name, uid, gid) changes the user and group
  1140.  * ownerships of a file to "uid" and "gid" respectively.
  1141.  */
  1142.  
  1143. long ARGS_ON_STACK
  1144. f_chown(name, uid, gid)
  1145.     const char *name;
  1146.     int uid, gid;
  1147. {
  1148.     fcookie fc;
  1149.     XATTR xattr;
  1150.     long r;
  1151.  
  1152.     TRACE(("Fchown(%s, %d, %d)", name, uid, gid));
  1153.  
  1154.     r = path2cookie(name, follow_links, &fc);
  1155.     if (r) {
  1156.         DEBUG(("Fchown(%s): error %ld", name, r));
  1157.         return r;
  1158.     }
  1159.  
  1160. /* MiNT acts like _POSIX_CHOWN_RESTRICTED: a non-privileged process can
  1161.  * only change the ownership of a file that is owned by this user, to
  1162.  * the effective group id of the process
  1163.  */
  1164.     if (curproc->euid) {
  1165.         if (curproc->egid != gid)
  1166.             r = EACCDN;
  1167.         else
  1168.             r = (*fc.fs->getxattr)(&fc, &xattr);
  1169.         if (r) {
  1170.             DEBUG(("Fchown(%s): unable to get file attributes",name));
  1171.             release_cookie(&fc);
  1172.             return r;
  1173.         }
  1174.         if (xattr.uid != curproc->euid || xattr.uid != uid) {
  1175.             DEBUG(("Fchown(%s): not the file's owner",name));
  1176.             release_cookie(&fc);
  1177.             return EACCDN;
  1178.         }
  1179.     }
  1180.     r = (*fc.fs->chown)(&fc, uid, gid);
  1181.     release_cookie(&fc);
  1182.     return r;
  1183. }
  1184.  
  1185. /*
  1186.  * GEMDOS extension: Fchmod(file, mode) changes a file's access
  1187.  * permissions.
  1188.  */
  1189.  
  1190. long ARGS_ON_STACK
  1191. f_chmod(name, mode)
  1192.     const char *name;
  1193.     unsigned mode;
  1194. {
  1195.     fcookie fc;
  1196.     long r;
  1197.     XATTR xattr;
  1198.  
  1199.     TRACE(("Fchmod(%s, %o)", name, mode));
  1200.     r = path2cookie(name, follow_links, &fc);
  1201.     if (r) {
  1202.         DEBUG(("Fchmod(%s): error %ld", name, r));
  1203.         return r;
  1204.     }
  1205.     r = (*fc.fs->getxattr)(&fc, &xattr);
  1206.     if (r) {
  1207.         DEBUG(("Fchmod(%s): couldn't get file attributes",name));
  1208.     }
  1209.     else if (curproc->euid && curproc->euid != xattr.uid) {
  1210.         DEBUG(("Fchmod(%s): not the file's owner",name));
  1211.         r = EACCDN;
  1212.     } else {
  1213.         r = (*fc.fs->chmode)(&fc, mode & ~S_IFMT);
  1214.         if (r) DEBUG(("Fchmod: error %ld", r));
  1215.     }
  1216.     release_cookie(&fc);
  1217.     return r;
  1218. }
  1219.  
  1220. /*
  1221.  * GEMDOS extension: Dlock(mode, dev): locks or unlocks access to
  1222.  * a BIOS device. "mode" bit 0 is 0 for unlock, 1 for lock; "dev" is a
  1223.  * BIOS device (0 for A:, 1 for B:, etc.).
  1224.  *
  1225.  * Returns: 0 if the operation was successful
  1226.  *          EACCDN if a lock attempt is made on a drive that is being
  1227.  *            used
  1228.  *        ELOCKED if the drive is locked by another process
  1229.  *        ENSLOCK if a program attempts to unlock a drive it
  1230.  *            hasn't locked.
  1231.  * ++jr: if mode bit 1 is set, then instead of returning ELOCKED the
  1232.  * pid of the process which has locked the drive is returned (unless
  1233.  * it was locked by pid 0, in which case ELOCKED is still returned).
  1234.  */
  1235.  
  1236. PROC *dlockproc[NUM_DRIVES];
  1237.  
  1238. long ARGS_ON_STACK
  1239. d_lock(mode, dev)
  1240.     int mode, dev;
  1241. {
  1242.     PROC *p;
  1243.     FILEPTR *f;
  1244.     int i;
  1245.     extern int aliasdrv[];
  1246.  
  1247.     TRACE(("Dlock(%x,%c:)", mode, dev+'A'));
  1248.     if (dev < 0 || dev >= NUM_DRIVES) return EDRIVE;
  1249.     if (aliasdrv[dev]) {
  1250.         dev = aliasdrv[dev] - 1;
  1251.         if (dev < 0 || dev >= NUM_DRIVES)
  1252.             return EDRIVE;
  1253.     }
  1254.     if ( (mode&1) == 0) {    /* unlock */
  1255.         if (dlockproc[dev] == curproc) {
  1256.             dlockproc[dev] = 0;
  1257.             changedrv(dev);
  1258.             return 0;
  1259.         }
  1260.         DEBUG(("Dlock: no such lock"));
  1261.         return ENSLOCK;
  1262.     }
  1263.  
  1264. /* code for locking */
  1265. /* is the drive already locked? */
  1266.     if (dlockproc[dev]) {
  1267.         DEBUG(("Dlock: drive already locked"));
  1268.         if (dlockproc[dev] == curproc) return 0;
  1269.         if (dlockproc[dev]->pid == 0) return ELOCKED;
  1270.         return (mode & 2) ? dlockproc[dev]->pid : ELOCKED;
  1271.     }
  1272. /* see if the drive is in use */
  1273.     for (p = proclist; p; p = p->gl_next) {
  1274.         if (p->wait_q == ZOMBIE_Q || p->wait_q == TSR_Q)
  1275.             continue;
  1276.         for (i = MIN_HANDLE; i < MAX_OPEN; i++) {
  1277.             if ( ((f = p->handle[i]) != 0) && (f->fc.dev == dev) ) {
  1278.         DEBUG(("Dlock: process %d has an open handle on the drive", p->pid));
  1279.                 if (p->pid == 0) return EACCDN;
  1280.                 return (mode & 2) ? p->pid : EACCDN;
  1281.             }
  1282.         }
  1283.     }
  1284.  
  1285. /* if we reach here, the drive is not in use */
  1286. /* we lock it by setting dlockproc and by setting all root and current
  1287.  * directories referring to the device to a null file system
  1288.  */
  1289.     for (p = proclist; p; p = p->gl_next) {
  1290.         for (i = 0; i < NUM_DRIVES; i++) {
  1291.             if (p->root[i].dev == dev) {
  1292.                 release_cookie(&p->root[i]);
  1293.                 p->root[i].fs = 0;
  1294.             }
  1295.             if (p->curdir[i].dev == dev) {
  1296.                 release_cookie(&p->curdir[i]);
  1297.                 p->curdir[i].fs = 0;
  1298.             }
  1299.         }
  1300.     }
  1301.  
  1302.     dlockproc[dev] = curproc;
  1303.     return 0;
  1304. }
  1305.